Go实战(五) 函数
函数声明
函数的声明形式如下:
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) { //这里是处理逻辑代码 //返回多个值 return value1, value2 }
其中, 函数返回的变量和类型中, 只写类型不写变量也是可以的. 如果返回值有多个, 需要用括号括起来, 只有一个则不用括号.
例子:
package main import "fmt" func max(a, b int) int { if a > b { return a } return b } func main() { x := 3 y := 4 z := 5 maxxy := max(x, y) maxxz := max(x, z) fmt.Printf("max(%d, %d) = %d\n", x, y, maxxy) fmt.Printf("max(%d, %d) = %d\n", x, z, maxxz) }
多个返回值
package main import "fmt" func SumAndProduct(a, b int) (int, int) { return a + b, a * b } func main() { x := 10 y := 2 sum, product := SumAndProduct(x, y) fmt.Printf("sum is %d\n", sum) fmt.Printf("product is %d\n", product) }
注意, 有多个返回值时, 函数声明时, 返回值列表要用括号括起来.
可变参数
拥有可变参数的函数声明形式:
func myFunc(arg...int) {}
例子:
package main import "fmt" func sum(vals ...int) int { total := 0 for _, val := range vals { total += val } return total } func main() { fmt.Printf("sum is %d\n", sum(1, 2)) fmt.Printf("sum is %d\n", sum(1, 2, 3)) }
在函数体中, vals 是一个 slice.
传值与传指针
传值
Go 里面给函数传递参数的形式是值传递, 所以在函数内部对参数的修改, 不影响函数外部的值. 如:
package main import "fmt" func sum(a, b int) int { a = a + 1 return a + b } func main() { x := 2 y := 3 fmt.Printf("x is %d\n", x) // 2 total := sum(x, y) fmt.Printf("total is %d\n", total) // 5 fmt.Printf("x is %d\n", x) // 2 }
可以看到, 虽然在函数 sum() 内部, 对第一个参数进行了加 1, 但是在函数外部, x 的值仍然是 2, 而不是 3. 因为在调用函数 sum() 时, 接收的参数其实是 x 的拷贝.
传指针
变量在内存中是存放在某个地址上的, 如果有这个地址, 就能修改这个值. 上面的例子中, 修改不了的原因, 是因为值一样, 但地址不同, 修改的是另一个地址的值. 例子如下:
package main import "fmt" func sum(a *int, b int) int { *a = *a + 1 return *a + b } func main() { x := 2 y := 3 fmt.Printf("x is %d\n", x) // 2 total := sum(&x, y) fmt.Printf("total is %d\n", total) // 6 fmt.Printf("x is %d\n", x) // 3 }
可以看到, 通过传递指针, 实现了传递的数是同一个数.
传指针的好处很多. 如, 传指针可以提高程序的效率, 如果有一个体积很大的结构体, 传值相当于一次拷贝, 会浪费很多内存; 传指针可以使多个函数可以操作同一个对象.
Go 语言中的 channel, slice, map 的实现类似指针, 可以直接传递, 不用先取地址, 但是如果要改变 slice 的长度, 仍需先取地址.
defer
defer 是 Go 的关键字, defer 后面跟着的语句, 会在函数返回之前调用. 如:
func test() int { fmt.Println("test 0") defer fmt.Println("test 1") fmt.Println("test 2") return 0 } // test 0 // test 2 // test 1
defer 常用于资源的释放. 如, 当打开某些资源时, 遇到错误要提前返回, 但是在返回前, 需要先关闭对应的资源, 否则会造成资源泄露等问题. 一般的写法如下:
func ReadWrite() bool { file.Open("file") // 做一些工作 if failureX { file.Close() return false } if failureY { file.Close() return false } file.Close() return true }
可以看出, 上面的程序里, 有很多重复的代码 file.Close(). 使用 defer 可以改善程序结构:
func ReadWrite() bool { file.Open("file") defer file.Close() if failureX { return false } if failureY { return false } return true }
如果有多个 defer 语句, 则后进先出. 如:
for i := 0; i < 6; i++ { defer fmt.Println(i) } // 5 4 3 2 1 0
函数作为值和类型
在 Go 中, 函数也是一种变量, 可以通过 type 来定义它, 在定义时会写明参数和返回值, 如果某个函数的参数和返回值与它相同, 就是同一个类型. 如:
package main import "fmt" type testInt func(int) bool // 声明了一个函数类型 func isOdd(integer int) bool { if integer%2 == 0 { return false } return true } func isEven(integer int) bool { if integer%2 == 0 { return true } return false } // 声明的函数类型在这个地方当做了一个参数 func filter(slice []int, f testInt) []int { var result []int for _, value := range slice { if f(value) { result = append(result, value) } } return result } func main(){ slice := []int {1, 2, 3, 4, 5, 7} fmt.Println("slice = ", slice) odd := filter(slice, isOdd) // 函数当做值来传递了 fmt.Println("Odd elements of slice are: ", odd) even := filter(slice, isEven) // 函数当做值来传递了 fmt.Println("Even elements of slice are: ", even) }
函数的这种用法对于编写通用接口非常有用, 我们可以使用相同类型的参数和返回值, 编写不同的逻辑, 使得程序变得更加灵活
.
Generated by Emacs 25.x(Org mode 8.x)
Copyright © 2014 - Pinvon - Powered by EGO